package com.pzy.base.handler;

import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.ObjectError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.resource.NoResourceFoundException;

import com.pzy.base.dto.ApiResponse;
import com.pzy.base.exception.BizException;
import com.pzy.base.util.RequestBodyContextUtil;

import jakarta.servlet.http.HttpServletRequest;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;
import lombok.extern.slf4j.Slf4j;

@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {

    /**
     * 构建发生异常时的请求uri信息
     * 
     * @param httpServletRequest
     * @return
     */
    private RequestUriInfo buildExpReqURI(HttpServletRequest httpServletRequest) {
        String reqUrl = String.format("%s %s %s", httpServletRequest.getMethod(), httpServletRequest.getContentType(),
                httpServletRequest.getRequestURI());
        String qs = httpServletRequest.getQueryString();
        qs = qs == null ? "" : qs;
        String queryBodyInfo = String.format(", query参数:[ %s ], request body参数:[ %s ]", qs,
                RequestBodyContextUtil.getParam());
        RequestUriInfo requestUriInfo = RequestUriInfo.builder().shortUri(reqUrl)
                .fullUri(String.format("%s %s", reqUrl, queryBodyInfo)).build();

        return requestUriInfo;
    }

    /**
     * 捕获参数绑定异常（如 @RequestBody 校验失败）
     * 
     * @param ex
     * @return
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ApiResponse<String> handleValidationExceptions(MethodArgumentNotValidException ex,
            HttpServletRequest httpServletRequest) {
        String errorMessage = ex.getBindingResult()
                .getAllErrors()
                .stream()
                .map(ObjectError::getDefaultMessage)
                .findFirst()
                .orElse("参数校验失败");
        RequestUriInfo requestUriInfo = buildExpReqURI(httpServletRequest);
        log.warn("请求 [{}] 参数校验失败: {}", requestUriInfo.getFullUri(), errorMessage);
        return ApiResponse.error(4000, errorMessage);
    }

    /**
     * 捕获资源未找到异常
     * 
     * @param ex
     * @return
     */
    @ExceptionHandler(NoResourceFoundException.class)
    public ApiResponse<String> handleNotFoundException(NoResourceFoundException ex,
            HttpServletRequest httpServletRequest) {
        RequestUriInfo requestUriInfo = buildExpReqURI(httpServletRequest);
        log.error(String.format("请求 [%s], 捕获资源未找到异常: ", requestUriInfo.getFullUri()), ex);
        return ApiResponse.error(404, String.format("资源未找到: %s", ex.getMessage()));
    }

    /**
     * 捕获请求方式异常
     * 
     * @param ex
     * @return
     */
    @ExceptionHandler(HttpMessageNotReadableException.class)
    public ApiResponse<String> handleHttpMessageNotReadableException(HttpMessageNotReadableException ex,
            HttpServletRequest httpServletRequest) {
        RequestUriInfo requestUriInfo = buildExpReqURI(httpServletRequest);
        log.error(String.format("请求 [%s], 捕获非预期的http请求参数异常: ", requestUriInfo.getFullUri()), ex);
        return ApiResponse.error(400, String.format("非预期的http请求参数异常: %s", ex.getMessage()));
    }

    /**
     * 捕获请求方式异常
     * 
     * @param ex
     * @return
     */
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public ApiResponse<String> handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException ex,
            HttpServletRequest httpServletRequest) {
        RequestUriInfo requestUriInfo = buildExpReqURI(httpServletRequest);
        log.error(String.format("请求 [%s], 调用端使用了不支持的请求方式: ", requestUriInfo.getFullUri()), ex);
        return ApiResponse.error(405, String.format("不支持的请求方式: %s", ex.getMessage()));
    }

    /**
     * 捕获预期的系统业务异常
     * 
     * @param ex
     * @return
     */
    @ExceptionHandler(BizException.class)
    public ApiResponse<String> handleBizException(BizException ex,
            HttpServletRequest httpServletRequest) {
        RequestUriInfo requestUriInfo = buildExpReqURI(httpServletRequest);
        log.warn(String.format("请求 [%s], 捕获预期系统业务异常: ", requestUriInfo.getFullUri()), ex);
        return ApiResponse.bizError(ex.getMessage());
    }

    /**
     * 捕获运行时异常
     * 
     * @param ex
     * @return
     */
    @ExceptionHandler(RuntimeException.class)
    public ApiResponse<String> handleRuntimeException(RuntimeException ex,
            HttpServletRequest httpServletRequest) {
        RequestUriInfo requestUriInfo = buildExpReqURI(httpServletRequest);
        log.error(String.format("请求 [%s], 捕获运行时异常: ", requestUriInfo.getFullUri()), ex);
        return ApiResponse.error(500, String.format("系统内部错误：%s", ex.getMessage()));
    }

    /**
     * 捕获非运行时异常
     * 
     * @param ex
     * @return
     */
    @ExceptionHandler(Exception.class)
    public ApiResponse<String> handleException(Exception ex,
            HttpServletRequest httpServletRequest) {
        RequestUriInfo requestUriInfo = buildExpReqURI(httpServletRequest);
        log.error(String.format("请求 [%s], 捕获异常: ", requestUriInfo.getFullUri()), ex);
        return ApiResponse.error(500, String.format("系统内部错误：%s", ex.getMessage()));
    }

}

@Setter
@Getter
@Accessors(chain = true)
@Builder
class RequestUriInfo {

    @Builder.Default
    private String shortUri = "";

    @Builder.Default
    private String fullUri = "";
}